﻿namespace PugLib.IO
{
	using System;
	using System.Diagnostics;
	using System.IO;
	using System.Globalization;

	public sealed class TemporaryFile : IDisposable
	{
		private readonly String _filePath;

		/// <summary>
		/// Creates a new instance of the file cloner class, automatically
		/// creating a temporary copy of the passed file.
		/// </summary>
		/// <param name="content">The content to write.</param>
		public TemporaryFile(String content)
		{
			_filePath = CreateFilePath();

			using (StreamWriter f = File.CreateText(FilePath))
			{
				f.Write(content ?? String.Empty);
			}
		}

		/// <summary>
		/// Creates a new instance of the file cloner class, automatically
		/// creating a temporary copy of the passed file.
		/// </summary>
		/// <param name="content">The content to write.</param>
		public TemporaryFile(Byte[] content)
		{
			_filePath = CreateFilePath();

			using (FileStream f = File.Create(FilePath))
			{
				if (content != null)
				{
					f.Write(content, 0, content.Length);
				}
			}
		}

		/// <summary>
		/// The complete path (drive, directory, file name, extension) to the
		/// temporary file.
		/// </summary>
		public String FilePath
		{
			get { return _filePath; }
		}

		#region IDisposable Members

		public void Dispose()
		{
			SafeDeleteFile(FilePath);
			GC.SuppressFinalize(this);
		}

		#endregion

		private static String CreateFilePath()
		{
			return Path.Combine(Path.GetTempPath(), String.Format(CultureInfo.InvariantCulture, "{0}.txt", Guid.NewGuid()));
		}

		private static Boolean SafeFileExists(String filePath)
		{
			return !String.IsNullOrEmpty(filePath) && File.Exists(filePath);
		}

		private static void SafeDeleteFile(String filePath)
		{
			Trace.TraceInformation(String.Format(CultureInfo.InvariantCulture, "About to safe-delete file '{0}'.", filePath));

			if (!String.IsNullOrEmpty(filePath) && SafeFileExists(filePath))
			{
				try
				{
					FileAttributes attributes = File.GetAttributes(filePath);

					// Remove read-only attributes.
					if ((attributes & FileAttributes.ReadOnly) != 0)
					{
						File.SetAttributes(filePath, attributes & (~(FileAttributes.ReadOnly)));
					}

					File.Delete(filePath);
				}
				catch (UnauthorizedAccessException x)
				{
					String newFilePath = String.Format(CultureInfo.InvariantCulture, "{0}.{1:N}.deleted", filePath, Guid.NewGuid());

					Trace.TraceWarning(String.Format(CultureInfo.InvariantCulture, "Caught UnauthorizedAccessException while deleting file '{0}'. Renaming now to '{1}'.", filePath, newFilePath), x);

					File.Move(filePath, newFilePath);
				}
				catch (IOException x)
				{
					String newFilePath = String.Format(CultureInfo.InvariantCulture, "{0}.{1:N}.deleted", filePath, Guid.NewGuid());
					Trace.TraceWarning(String.Format(CultureInfo.InvariantCulture, "Caught IOException while deleting file '{0}'. Renaming now to '{1}'.", filePath, newFilePath), x);
					File.Move(filePath, newFilePath);
				}
			}
			else
			{
				Trace.TraceInformation(String.Format(CultureInfo.InvariantCulture, "Not safe-deleting file '{0}', because the file does not exist.", filePath));
			}
		}
	}
}